package com.stars.easyms.datasource.mybatis;

import com.stars.easyms.datasource.EasyMsDataSourceFactory;
import com.stars.easyms.datasource.pointcut.*;
import org.apache.ibatis.binding.MapperProxy;
import org.apache.ibatis.session.SqlSession;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.support.Pointcuts;

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>className: EasyMsMapperProxy</p>
 * <p>description: EasyMs的mapper代理类</p>
 *
 * @author guoguifang
 * @version 1.2.2
 * @date 2019-08-12 12:04
 */
@SuppressWarnings("unchecked")
final class EasyMsMapperProxy<T> extends MapperProxy<T> {

    private final transient Set<PointcutAdvisor> pointcutAdvisorSet = new LinkedHashSet<>();

    EasyMsMapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map methodCache) {
        super(sqlSession, mapperInterface, methodCache);
        pointcutAdvisorSet.add(new FixedDataSourceMethodPointcutAdvisor());
        pointcutAdvisorSet.add(new FixedDataSourceTypePointcutAdvisor());
        pointcutAdvisorSet.add(new SpecifyDataSourceMethodPointcutAdvisor(EasyMsDataSourceFactory.getEasyMsMultiDataSource()));
        pointcutAdvisorSet.add(new SpecifyDataSourceTypePointcutAdvisor(EasyMsDataSourceFactory.getEasyMsMultiDataSource()));
        pointcutAdvisorSet.add(new EasyMsRepositoryPointcutAdvisor(EasyMsDataSourceFactory.getEasyMsMultiDataSource()));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        List<Object> chain = pointcutAdvisorSet.stream().
                filter(pointcutAdvisor -> Pointcuts.matches(pointcutAdvisor.getPointcut(), method, method.getDeclaringClass()))
                .map(PointcutAdvisor::getAdvice)
                .collect(Collectors.toList());
        return new EasyMsMybatisMethodInvocation(proxy, this, method, args, chain).proceed();
    }

    public Object proceed(Object proxy, Method method, Object[] args) throws Throwable {
        return super.invoke(proxy, method, args);
    }
}